import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.ensemble import IsolationForest
from sklearn.metrics import silhouette_score
import warnings
warnings.filterwarnings('ignore')

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

class DollarIndexAnalysis:
    """
    美元指数期货历史数据无监督学习分析类
    """
    
    def __init__(self, file_path):
        """
        初始化分析类
        
        Args:
            file_path: 数据文件路径
        """
        self.file_path = file_path
        self.data = None
        self.features = None
        self.scaled_features = None
        self.scaler = StandardScaler()
        
    def load_and_preprocess_data(self):
        """
        加载并预处理数据
        """
        print("加载并预处理数据...")
        # 读取CSV文件
        self.data = pd.read_csv(self.file_path)
        
        # 数据基本信息
        print(f"数据形状: {self.data.shape}")
        print("\n数据前5行:")
        print(self.data.head())
        
        # 处理日期列
        self.data['日期'] = pd.to_datetime(self.data['日期'], format='%Y年%m月%d日')
        
        # 处理交易量列，去除'K'并转换为数字
        self.data['交易量'] = self.data['交易量'].str.replace('K', '').astype(float) * 1000
        
        # 处理涨跌幅列，去除'%'并转换为数字
        self.data['涨跌幅'] = self.data['涨跌幅'].str.replace('%', '').astype(float)
        
        # 计算一些技术指标作为特征
        self.data['价格波动'] = self.data['高'] - self.data['低']
        self.data['价格变化'] = self.data['收盘'] - self.data['开盘']
        self.data['波动率'] = (self.data['高'] - self.data['低']) / self.data['开盘'] * 100
        
        # 选择特征列
        feature_columns = ['收盘', '开盘', '高', '低', '交易量', '涨跌幅', '价格波动', '价格变化', '波动率']
        self.features = self.data[feature_columns].copy()
        
        # 标准化特征
        self.scaled_features = self.scaler.fit_transform(self.features)
        
        print("\n预处理后特征:")
        print(self.features.head())
        
    def exploratory_data_analysis(self):
        """
        探索性数据分析
        """
        print("\n执行探索性数据分析...")
        
        # 设置图形大小
        plt.figure(figsize=(20, 15))
        
        # 1. 收盘价时间序列图
        plt.subplot(3, 2, 1)
        plt.plot(self.data['日期'], self.data['收盘'])
        plt.title('美元指数期货收盘价时间序列')
        plt.xlabel('日期')
        plt.ylabel('收盘价')
        plt.grid(True)
        
        # 2. 交易量柱状图
        plt.subplot(3, 2, 2)
        plt.bar(self.data['日期'], self.data['交易量'])
        plt.title('交易量分布')
        plt.xlabel('日期')
        plt.ylabel('交易量')
        plt.grid(True)
        
        # 3. 涨跌幅直方图
        plt.subplot(3, 2, 3)
        plt.hist(self.data['涨跌幅'], bins=30)
        plt.title('涨跌幅分布')
        plt.xlabel('涨跌幅(%)')
        plt.ylabel('频率')
        plt.grid(True)
        
        # 4. 波动率散点图
        plt.subplot(3, 2, 4)
        plt.scatter(self.data['涨跌幅'], self.data['波动率'])
        plt.title('涨跌幅与波动率关系')
        plt.xlabel('涨跌幅(%)')
        plt.ylabel('波动率(%)')
        plt.grid(True)
        
        # 5. 特征相关性热力图
        plt.subplot(3, 2, 5)
        corr_matrix = self.features.corr()
        sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f')
        plt.title('特征相关性热力图')
        
        plt.tight_layout()
        plt.savefig('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\eda_plots.png', dpi=300)
        plt.close()
        
        print("探索性分析图表已保存为 eda_plots.png")
    
    def kmeans_clustering(self):
        """
        K-means聚类分析
        """
        print("\n执行K-means聚类分析...")
        
        # 确定最佳聚类数
        inertias = []
        silhouette_scores = []
        K_range = range(2, 8)
        
        for k in K_range:
            kmeans = KMeans(n_clusters=k, random_state=42)
            kmeans.fit(self.scaled_features)
            inertias.append(kmeans.inertia_)
            silhouette_scores.append(silhouette_score(self.scaled_features, kmeans.labels_))
        
        # 绘制肘部图和轮廓系数图
        plt.figure(figsize=(15, 6))
        
        plt.subplot(1, 2, 1)
        plt.plot(K_range, inertias, 'bx-')
        plt.xlabel('聚类数 k')
        plt.ylabel('惯性 (Inertia)')
        plt.title('肘部法则确定最佳聚类数')
        plt.grid(True)
        
        plt.subplot(1, 2, 2)
        plt.plot(K_range, silhouette_scores, 'rx-')
        plt.xlabel('聚类数 k')
        plt.ylabel('轮廓系数')
        plt.title('轮廓系数确定最佳聚类数')
        plt.grid(True)
        
        plt.tight_layout()
        plt.savefig('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\clustering_evaluation.png', dpi=300)
        plt.close()
        
        # 选择最佳聚类数（这里选择轮廓系数最高的）
        best_k = K_range[np.argmax(silhouette_scores)]
        print(f"最佳聚类数: {best_k}")
        
        # 使用最佳聚类数进行聚类
        kmeans = KMeans(n_clusters=best_k, random_state=42)
        self.data['cluster'] = kmeans.fit_predict(self.scaled_features)
        
        # 分析每个聚类的特征
        print("\n各聚类的特征统计:")
        cluster_analysis = self.data.groupby('cluster')[['收盘', '交易量', '涨跌幅', '波动率']].mean()
        print(cluster_analysis)
        
        # 可视化聚类结果
        plt.figure(figsize=(15, 10))
        
        # 时间序列上的聚类分布
        plt.subplot(2, 2, 1)
        for cluster in range(best_k):
            cluster_data = self.data[self.data['cluster'] == cluster]
            plt.scatter(cluster_data['日期'], cluster_data['收盘'], label=f'聚类 {cluster}')
        plt.title(f'K-means聚类结果 (k={best_k})')
        plt.xlabel('日期')
        plt.ylabel('收盘价')
        plt.legend()
        plt.grid(True)
        
        # 涨跌幅 vs 波动率 散点图
        plt.subplot(2, 2, 2)
        for cluster in range(best_k):
            cluster_data = self.data[self.data['cluster'] == cluster]
            plt.scatter(cluster_data['涨跌幅'], cluster_data['波动率'], label=f'聚类 {cluster}')
        plt.title('涨跌幅 vs 波动率 聚类分布')
        plt.xlabel('涨跌幅(%)')
        plt.ylabel('波动率(%)')
        plt.legend()
        plt.grid(True)
        
        # 交易量分布
        plt.subplot(2, 2, 3)
        for cluster in range(best_k):
            cluster_data = self.data[self.data['cluster'] == cluster]
            plt.hist(cluster_data['交易量'], alpha=0.5, label=f'聚类 {cluster}')
        plt.title('各聚类交易量分布')
        plt.xlabel('交易量')
        plt.ylabel('频率')
        plt.legend()
        plt.grid(True)
        
        # 聚类大小饼图
        plt.subplot(2, 2, 4)
        cluster_sizes = self.data['cluster'].value_counts()
        plt.pie(cluster_sizes, labels=[f'聚类 {i}' for i in range(best_k)], autopct='%1.1f%%')
        plt.title('各聚类样本比例')
        
        plt.tight_layout()
        plt.savefig('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\kmeans_results.png', dpi=300)
        plt.close()
        
        print("K-means聚类结果已保存为 kmeans_results.png")
    
    def pca_analysis(self):
        """
        PCA主成分分析
        """
        print("\n执行PCA主成分分析...")
        
        # 执行PCA
        pca = PCA(n_components=2)  # 降到2维以便可视化
        pca_result = pca.fit_transform(self.scaled_features)
        
        # 将PCA结果添加到数据中
        self.data['pca1'] = pca_result[:, 0]
        self.data['pca2'] = pca_result[:, 1]
        
        # 计算解释方差比
        explained_variance = pca.explained_variance_ratio_
        print(f"PCA解释方差比: {explained_variance}")
        print(f"累计解释方差: {sum(explained_variance):.4f}")
        
        # 可视化PCA结果
        plt.figure(figsize=(15, 10))
        
        # PCA散点图
        plt.subplot(2, 2, 1)
        plt.scatter(self.data['pca1'], self.data['pca2'], c=self.data.get('cluster', 'b'), cmap='viridis')
        plt.title('PCA降维结果')
        plt.xlabel(f'主成分1 ({explained_variance[0]:.2%})')
        plt.ylabel(f'主成分2 ({explained_variance[1]:.2%})')
        plt.grid(True)
        
        # 特征载荷图
        plt.subplot(2, 2, 2)
        feature_names = self.features.columns
        for i, feature in enumerate(feature_names):
            plt.arrow(0, 0, pca.components_[0, i], pca.components_[1, i], 
                      head_width=0.05, head_length=0.05, fc='r', ec='r')
            plt.text(pca.components_[0, i] * 1.15, pca.components_[1, i] * 1.15, 
                     feature, color='r')
        plt.xlim(-1, 1)
        plt.ylim(-1, 1)
        plt.title('PCA特征载荷')
        plt.xlabel('主成分1')
        plt.ylabel('主成分2')
        plt.grid(True)
        
        # 解释方差累积图
        plt.subplot(2, 2, 3)
        pca_full = PCA().fit(self.scaled_features)
        cumulative_variance = np.cumsum(pca_full.explained_variance_ratio_)
        plt.plot(range(1, len(cumulative_variance) + 1), cumulative_variance, 'bx-')
        plt.axhline(y=0.9, color='r', linestyle='--')
        plt.title('PCA累计解释方差')
        plt.xlabel('主成分数量')
        plt.ylabel('累计解释方差')
        plt.grid(True)
        
        # PCA时间序列分析
        plt.subplot(2, 2, 4)
        plt.plot(self.data['日期'], self.data['pca1'], label='主成分1')
        plt.plot(self.data['日期'], self.data['pca2'], label='主成分2')
        plt.title('主成分时间序列变化')
        plt.xlabel('日期')
        plt.ylabel('主成分值')
        plt.legend()
        plt.grid(True)
        
        plt.tight_layout()
        plt.savefig('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\pca_results.png', dpi=300)
        plt.close()
        
        print("PCA分析结果已保存为 pca_results.png")
    
    def anomaly_detection(self):
        """
        异常检测
        """
        print("\n执行异常检测...")
        
        # 使用隔离森林进行异常检测
        iso_forest = IsolationForest(contamination=0.1, random_state=42)
        self.data['anomaly'] = iso_forest.fit_predict(self.scaled_features)
        
        # -1表示异常点，1表示正常点
        anomaly_count = (self.data['anomaly'] == -1).sum()
        print(f"检测到的异常点数量: {anomaly_count}")
        
        # 可视化异常检测结果
        plt.figure(figsize=(15, 10))
        
        # 收盘价时间序列中的异常点
        plt.subplot(2, 2, 1)
        normal = self.data[self.data['anomaly'] == 1]
        anomaly = self.data[self.data['anomaly'] == -1]
        plt.plot(normal['日期'], normal['收盘'], 'b-', label='正常点')
        plt.scatter(anomaly['日期'], anomaly['收盘'], color='r', label='异常点')
        plt.title('收盘价时间序列中的异常点')
        plt.xlabel('日期')
        plt.ylabel('收盘价')
        plt.legend()
        plt.grid(True)
        
        # 涨跌幅 vs 波动率散点图中的异常点
        plt.subplot(2, 2, 2)
        plt.scatter(normal['涨跌幅'], normal['波动率'], color='b', label='正常点')
        plt.scatter(anomaly['涨跌幅'], anomaly['波动率'], color='r', label='异常点')
        plt.title('涨跌幅 vs 波动率散点图中的异常点')
        plt.xlabel('涨跌幅(%)')
        plt.ylabel('波动率(%)')
        plt.legend()
        plt.grid(True)
        
        # PCA散点图中的异常点
        plt.subplot(2, 2, 3)
        plt.scatter(normal['pca1'], normal['pca2'], color='b', label='正常点')
        plt.scatter(anomaly['pca1'], anomaly['pca2'], color='r', label='异常点')
        plt.title('PCA散点图中的异常点')
        plt.xlabel('主成分1')
        plt.ylabel('主成分2')
        plt.legend()
        plt.grid(True)
        
        # 异常点详细信息
        plt.subplot(2, 2, 4)
        plt.axis('off')
        anomaly_info = anomaly[['日期', '收盘', '交易量', '涨跌幅', '波动率']].copy()
        anomaly_info['日期'] = anomaly_info['日期'].dt.strftime('%Y-%m-%d')
        table = plt.table(cellText=anomaly_info.values, colLabels=anomaly_info.columns, loc='center')
        table.auto_set_font_size(False)
        table.set_fontsize(8)
        table.scale(1, 1.5)
        plt.title('异常点详细信息')
        
        plt.tight_layout()
        plt.savefig('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\anomaly_detection_results.png', dpi=300)
        plt.close()
        
        # 保存异常点数据
        anomaly.to_csv('c:\\Users\\YEDX\\financial-analysis3\\chapter6\\anomalies.csv', index=False)
        print("异常点数据已保存为 anomalies.csv")
        print("异常检测结果已保存为 anomaly_detection_results.png")
    
    def run_full_analysis(self):
        """
        运行完整的分析流程
        """
        print("开始美元指数期货历史数据无监督学习分析")
        
        # 1. 数据加载和预处理
        self.load_and_preprocess_data()
        
        # 2. 探索性数据分析
        self.exploratory_data_analysis()
        
        # 3. K-means聚类分析
        self.kmeans_clustering()
        
        # 4. PCA降维分析
        self.pca_analysis()
        
        # 5. 异常检测
        self.anomaly_detection()
        
        print("\n分析完成！所有结果已保存。")

if __name__ == "__main__":
    # 数据文件路径
    file_path = "c:\\Users\\YEDX\\financial-analysis3\\chapter6\\美元指数期货历史数据.csv"
    
    # 创建分析实例并运行
    analysis = DollarIndexAnalysis(file_path)
    analysis.run_full_analysis()